<?php

/**
 * @author Marc André Audet <root@manhim.net>
 * @copyright Copyright(c) 2009-2010 Marc André Audet
 * @version 1.0.5.23
 * @license http://www.gnu.org/licenses/gpl.txt The GNU General Public License version 3
 *
 *   Templates class, made for Manhim's Object Oriented framework version 2
 *   Version consist of:
 *   [Major Version].[Number of years since 2010].[Month].[Date]
 *   The informations are based on the latest edit of this file, not the whole project.
 *
 *   Copyright (C) 2009-2010 Marc André Audet
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
require_once 'Exceptions.class.php';

/**
 * The Templates object is the parent of the Template object.
 * It is used to display templates in a secure and controlled way as well as having the ability to use PHP code in the templates files.
 * @author Marc André Audet
 *
 */
class Templates
{

    /** @var string */
    private $_Path;
    /** @var boolean */
    private $_Initialized = false;

    /**
     * Templates object constructor.
     * If the $path string is empty the object wont be initialized and will need to be manually.
     * @param string $path
     */
    function __construct($path = null)
    {
        if ($path !== null)
        {
            $this->SetPath($path);
            $this->Initialize();
        }
    }

    /**
     * Initialize the Templates object.
     * @throws TemplatesInitializedException
     */
    function Initialize()
    {
        if (!$this->Initialized())
        {
            $this->TestPath();
            $this->_Initialized = true;
        }
        else
        {
            throw new TemplatesInitializedException('Trying to initalize an already initialized templates wrapper.');
        }
    }

    /**
     * Initialized boolean accessor.
     * @return boolean
     */
    function Initialized()
    {
        return $this->_Initialized;
    }

    /**
     * Create and return a new Template object.
     * @param string $filename
     * @throws TemplatesNotInitializedException
     * @return Template
     */
    function Template($filename)
    {
        if ($this->Initialized())
        {
            return new Template($this, $filename);
        }
        else
        {
            throw new TemplatesNotInitializedException('Tryed to assign a template in a not initialized templates wrapper.');
        }
    }

    /**
     * Path string accessor.
     * @return string
     */
    function GetPath()
    {
        return $this->_Path;
    }

    /**
     * Set the Path string and test it.
     * @param string $path
     */
    function SetPath($path)
    {
        $this->_Path = realpath($path) . '/';
        $this->TestPath();
    }

    /**
     * Private method to test the Path variable.
     * @throws TemplatesException
     */
    private function TestPath()
    {
        if ($this->GetPath() == '')
        {
            throw new TemplatesException('The selected path is invalid.');
        }
        else if (!is_dir($this->GetPath()))
        {
            throw new TemplatesException('The selected path does not exists.');
        }
        else if (!is_readable($this->GetPath()))
        {
            throw new TemplatesException('The selected path is not readable.');
        }
    }

}

/**
 * The Template object is the child of the Templates object.
 * It is used to display templates in a secure and controled way as well as having the ability to use PHP code in the templates files.
 * @author Marc André Audet
 */
class Template
{

    /** @var $_Templates Templates */
    private $_Templates;
    /** @var $_Filename string */
    private $_Filename;
    /** @var $_Initialized boolean */
    private $_Initialized = false;
    /** @var $_Vars array */
    private $_Vars = array();

    /**
     * Template object constructor.
     * Requires a parent Templates object.
     * If the $filename string is empty the object wont be initialized and will need to be manually.
     * @param Templates $templates
     * @param string $filename
     */
    function __construct(Templates &$templates, $filename = null)
    {
        $this->SetTemplates($templates);

        if ($filename !== null)
        {
            $this->SetFilename($filename);
            $this->Initialize();
        }
    }

    /**
     * Initialize the Template object.
     * @throws TemplateInitializedException
     */
    function Initialize()
    {
        if (!$this->Initialized())
        {
            $this->TestFilename();
            $this->_Initialized = true;
        }
        else
        {
            throw new TemplateInitializedException('Tryed to initialize an already initialized template.');
        }
    }

    /**
     * Initialized boolean accessor.
     * @return boolean
     */
    function Initialized()
    {
        return $this->_Initialized;
    }

    /**
     * Merge the currently assigned array with the input array.
     * @param array $array
     * @throws TemplateException
     * @throws TemplateNotInitializedException
     * @return Template
     */
    function Assign($array)
    {
        if ($this->Initialized())
        {
            if (is_array($array))
            {
                $this->_Vars = array_merge($this->_Vars, $array);
            }
            else
            {
                throw new TemplateException('Invalid template variables. Array type expected.');
            }

            return $this;
        }
        else
        {
            throw new TemplateNotInitializedException('Tryed to assign variables in a not initialized template.');
        }
    }

    /**
     * If the $return boolean is set to false, the content will be echoed directly, else it will be returned.
     * @param boolean $return
     * @throws TemplateNotInitializedException
     * @return null string
     */
    function Display($return = false)
    {
        if ($this->Initialized())
        {
            ob_start();
            self::_Display($this);
            $display = ob_get_contents();
            ob_end_clean();

            if ($return == false)
            {
                echo $display;
                $display = null;
            }
            else
            {
                return $display;
            }
        }
        else
        {
            throw new TemplateNotInitializedException('Tryed to display in a not initialized template.');
        }
    }

    /**
     * Include the Template object's file to fetch the content.
     * Used by the Display method.
     * @param Template $_Template
     * @throws TemplateNotInitializedException
     */
    static private function _Display($_Template)
    {
        if ($_Template->Initialized())
        {
            include $_Template->GetTemplates()->GetPath() . $_Template->GetFilename();
        }
        else
        {
            throw new TemplateNotInitializedException('Tryed to display in a not initialized template.');
        }
    }

    /**
     * Templates object accessor.
     * @return Templates
     */
    function GetTemplates()
    {
        return $this->_Templates;
    }

    /**
     * Filename string accessor.
     * @return string
     */
    function GetFilename()
    {
        return $this->_Filename;
    }

    /**
     * Set the Templates object and test it.
     * @param Templates $templates
     */
    private function SetTemplates(Templates &$templates)
    {
        $this->_Templates = &$templates;
        $this->TestTemplates();
    }

    /**
     * Set the Filename string and test it.
     * @param string $filename
     */
    function SetFilename($filename)
    {
        $this->_Filename = $filename;
        $this->TestFilename();
    }

    /**
     * Private method to test the Templates object.
     * @throws TemplateException
     */
    private function TestTemplates()
    {
        if (!($this->GetTemplates() instanceof Templates))
        {
            throw new TemplateException('The templates wrapper for this template is invalid.');
        }
        else if (!$this->GetTemplates()->Initialized())
        {
            throw new TemplateException('The templates wrapper for this template is not initialized.');
        }
    }

    /**
     * Private method to test the Filename variable.
     * @throws TemplateException
     */
    private function TestFilename()
    {
        if (!is_file($this->GetTemplates()->GetPath() . $this->GetFilename()))
        {
            throw new TemplateException('The selected filename does not exists.');
        }
        else if (!is_readable($this->GetTemplates()->GetPath() . $this->GetFilename()))
        {
            throw new TemplateException('The selected filename is not readable.');
        }
    }

    /**
     * Overide the getter function.
     * @param string $member
     * @return var
     */
    function __get($member)
    {
        return $this->_Vars['' . $member . ''];
    }

    /**
     * Overide the setter function to prevent variables writing.
     * @param string $member
     * @param string $value
     */
    function __set($member, $value)
    {
        return;
    }

}

/**
 * Templates object exception.
 * General purpose exception.
 * @author Marc André Audet
 *
 */
class TemplatesException extends CustomException
{

}

/**
 * Templates object exception.
 * Throwed when the Templates object haven't been initialized.
 * @author Marc André Audet
 *
 */
class TemplatesNotInitializedException extends CustomException
{

}

/**
 * Templates object exceptions.
 * Throwed when the Templates object have been initialized already.
 * @author Marc André Audet
 *
 */
class TemplatesInitializedException extends CustomException
{

}

/**
 * Template object exceptions.
 * General purpose exception.
 * @author Marc André Audet
 *
 */
class TemplateException extends CustomException
{

}

/**
 * Template object exceptions.
 * Throwed when the Template object haven't been initialized.
 * @author Marc André Audet
 *
 */
class TemplateNotInitializedException extends CustomException
{

}

/**
 * Template object exceptions.
 * Throwed when the Template object have been initialized already.
 * @author Marc André Audet
 *
 */
class TemplateInitializedException extends CustomException
{

}

?>